#include "item/polygonitem.h"

#include <QStyleOptionGraphicsItem>
#include <QPainter>

namespace SymbolEditor
{

    GraphicsPolygonItem::GraphicsPolygonItem(GraphicsItem *parent):
        GraphicsItem(parent),
        m_fillRule(Qt::OddEvenFill)
    {

    }

    GraphicsPolygonItem::~GraphicsPolygonItem()
    {

    }

    Qt::FillRule GraphicsPolygonItem::fillRule() const
    {
        return m_fillRule;
    }

    QPolygonF GraphicsPolygonItem::polygon() const
    {
        return m_polygon;
    }

    void GraphicsPolygonItem::setFillRule(Qt::FillRule fillRule)
    {
        if (m_fillRule == fillRule)
        {
            return;
        }

        m_fillRule = fillRule;
        update();
    }

    void GraphicsPolygonItem::setPolygon(QPolygonF polygon)
    {
        if (m_polygon == polygon)
        {
            return;
        }

        removeAllHandles();

        prepareGeometryChange();
        m_polygon = polygon;
        for (int i = 0; i < polygon.count(); i++)
        {
            addHandle(i, Handle::Role::Move, Handle::Shape::Circle, polygon[i]);
        }
        m_boundingRect = QRectF();
        update();
    }

    void GraphicsPolygonItem::addPoint(const QPointF &pos)
    {
        addHandle(m_polygon.count(), Handle::Role::Move, Handle::Shape::Circle, pos);
        m_polygon.append(pos);
        handleToPolygon();
    }

    void GraphicsPolygonItem::movePoint(int idx, const QPointF &pos)
    {
        blockItemNotification();
        m_idToHandle[idx]->setPos(pos);
        unblockItemNotification();
        handleToPolygon();
    }

    void GraphicsPolygonItem::handleToPolygon()
    {
        prepareGeometryChange();
        m_polygon = QPolygonF();
        for (int i = 0; i < m_idToHandle.count(); i++)
        {
            m_polygon.append(m_idToHandle[i]->pos());
        }
        m_boundingRect = QRectF();
        update();
    }

    void GraphicsPolygonItem::polygonToHandle()
    {
        blockItemNotification();
        for (int i = 0; i < m_polygon.count(); i++)
        {
            m_idToHandle[i]->setPos(m_polygon[i].x(),
                                    m_polygon[i].y());
        }
        unblockItemNotification();
    }

    QList<QLineF> GraphicsPolygonItem::toLines() const
    {
        QList<QLineF> result;
        for (int i = 0; i < m_polygon.count(); i++)
        {
            result << QLineF(m_polygon.value(i),
                             m_polygon.value((i+1) % m_polygon.count()));
        }
        return result;
    }

    QRectF GraphicsPolygonItem::boundingRect() const
    {
        if (m_boundingRect.isNull())
        {
            qreal pw = pen().style() == Qt::NoPen ? qreal(0) : pen().widthF();
            if (pw == 0.0)
            {
                m_boundingRect = m_polygon.boundingRect();
            }
            else
            {
                m_boundingRect = shape().controlPointRect();
            }
        }
        return m_boundingRect;
    }

    QPainterPath GraphicsPolygonItem::shape() const
    {
        QPainterPath path;
        path.addPolygon(polygon());
        return shapeFromPath(path, pen());
    }

    void GraphicsPolygonItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                                    QWidget *widget)
    {
        Q_UNUSED(option);
        Q_UNUSED(widget);
        painter->setPen(pen());
        painter->setBrush(brush());
        painter->drawPolygon(polygon(), fillRule());
    }

    GraphicsItem *GraphicsPolygonItem::clone()
    {
        GraphicsPolygonItem *item = new GraphicsPolygonItem();
        item->setPolygon(polygon());
        item->setFillRule(fillRule());
        GraphicsItem::cloneTo(item);
        return item;
    }

    void GraphicsPolygonItem::setProperty(PropertyId id, const QVariant &value)
    {
        switch (id)
        {
            case VerticesProperty:
            {
                auto vertices = value.value<QList<QPointF>>();
                setPolygon(QPolygonF(vertices.toVector()));
                break;
            }
            default:
                GraphicsItem::setProperty(id, value);
                break;
        }
    }

    QVariant GraphicsPolygonItem::itemChange(GraphicsItemChange change, const QVariant &value)
    {
        if (change == QGraphicsItem::ItemSelectedHasChanged)
        {
            for (Handle *handle : m_idToHandle)
            {
                handle->setVisible(isSelected());
            }
        }
        return value;
    }

    void GraphicsPolygonItem::itemNotification(IObservableItem *item)
    {
        Q_UNUSED(item);
        handleToPolygon();
    }

    QList<QPointF> GraphicsPolygonItem::endPoints() const
    {
        return m_polygon.toList();
    }

    QList<QPointF> GraphicsPolygonItem::midPoints() const
    {
        QList<QPointF> result;

        for (auto line: toLines())
        {
            result.append(line.pointAt(0.5));
        }
        return result;
    }

    QList<QPointF> GraphicsPolygonItem::centerPoints() const
    {
        return QList<QPointF>() << m_polygon.boundingRect().center();
    }

    QList<QPointF> GraphicsPolygonItem::nearestPoints(QPointF pos) const
    {
        QList<QPointF> result;
        for (auto line: toLines())
        {
            result << nearestPoint(line, mapFromScene(pos));
        }
        return result;
    }

}
